package vue.panneau2.vue;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.QuadCurve2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;

import vue.panneau2.modele.ModelePanneau2;
import vue.panneau2.modele.exception.IndiceIncorrectException;

public class PanneauRepresentation extends JPanel {
	private static final long serialVersionUID = 1L;
	private static final float TAILLELIGNE = 30f;
	private static final float TAILLECOL = 120f;
	private static final float DECALMATVERTICAL = 30f;
	private static final float DECALMATHORIZONTAL = 30f;
	private static final float ESPACEINTERLIGNE = 20f;
	private static final float ESPACEINTITULEMATRICE = 15f;
	private int nbLignes = 0;
	private int nbColonnes = 0;
	private ModelePanneau2 modelePanneau2;
	private int maxY = 200;
	private int maxX = 200;
	
	public PanneauRepresentation(ModelePanneau2 modelePanneau2) {
		this.modelePanneau2 = modelePanneau2;
		
		setVisible(true);
	}

	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2d = (Graphics2D)g;
		//Activation antialiasing
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

		try {
			if (modelePanneau2.getProblemeLineaire().isResolu()) {
				nbLignes = modelePanneau2.getProblemeLineaire().getNbLignesMatriceAugmentee();
				nbColonnes = modelePanneau2.getProblemeLineaire().getNbColonnesMatriceAugmentee();
				
				//L'étape -1, étape préliminaire, la première à être affichée
				if (modelePanneau2.getNumEtapeCourant() == -1) {
					//Texte d'explication
					g2d.setFont(new Font("Helvetica", Font.BOLD, 16));
					g2d.drawString("Système d'équation initial", DECALMATHORIZONTAL, DECALMATVERTICAL);
					
					g2d.setFont(new Font("Helvetica", Font.ITALIC, 14));
					float decalText = DECALMATVERTICAL+ESPACEINTERLIGNE;
					for (String phrase : modelePanneau2.getProblemeLineaire().afficheSystemeEquation()) {
						g2d.drawString(phrase, DECALMATHORIZONTAL, decalText);
						decalText += ESPACEINTERLIGNE;
					}
					
					g2d.setFont(new Font("Helvetica", Font.BOLD, 16));
					g2d.drawString("Après conversion vers une matrice augmentée, cela donne : ", DECALMATHORIZONTAL, DECALMATVERTICAL+decalText);
					
					//Intitulés de matrice
					dessineIntitules(g2d, 0.0f, DECALMATVERTICAL+decalText, false);
					//Matrice augmentée
					decalText += DECALMATVERTICAL+ESPACEINTITULEMATRICE;
					dessineMatriceAugmenteeEtape(g2d, 0.0f, decalText, 0);
					
					//Affichage de la solution de base réalisable courante
					decalText += 2*DECALMATVERTICAL+ESPACEINTITULEMATRICE+nbLignes*TAILLELIGNE;
					g2d.setFont(new Font("Helvetica", Font.ITALIC, 14));
					g2d.drawString("Associé à la solution de base réalisable : ", DECALMATHORIZONTAL, decalText);
					decalText += ESPACEINTERLIGNE;
					ArrayList<String> solBase = modelePanneau2.getProblemeLineaire().afficheSolutionBaseRealisableEtape(0);
					for (int i = 0; i < solBase.size(); i++) {
						g2d.drawString(solBase.get(i), DECALMATHORIZONTAL, decalText);
						decalText += ESPACEINTERLIGNE;
					}
					
					//Réglage de la taille max de la fenêtre
					maxX = (int)(nbColonnes*TAILLECOL+DECALMATHORIZONTAL);
					maxY = (int)decalText;
				}
				else
				//L'étape finale
				if (modelePanneau2.getNumEtapeCourant() == modelePanneau2.getProblemeLineaire().getnombreDeMatrices() - 1) {
					g2d.setFont(new Font("Helvetica", Font.BOLD, 16));
					g2d.drawString("Obtention de la matrice finale :", DECALMATHORIZONTAL, DECALMATVERTICAL);
					
					//Affiche intitulés et matrice augmentée
					dessineIntitules(g2d, 0.0f, DECALMATVERTICAL, false);
					dessineMatriceAugmenteeEtape(g2d, 0.0f, DECALMATVERTICAL+ESPACEINTITULEMATRICE, modelePanneau2.getProblemeLineaire().getnombreDeMatrices() - 1);
					
					g2d.setFont(new Font("Helvetica", Font.BOLD, 16));
					float decalText = DECALMATVERTICAL+ESPACEINTITULEMATRICE+nbLignes*TAILLELIGNE+ESPACEINTERLIGNE+50f;
					g2d.drawString("Obtention de la solution finale au problème avec les valeurs : ", DECALMATHORIZONTAL, decalText);
					
					g2d.setFont(new Font("Helvetica", Font.ITALIC, 14));
					//Affichage de la solution de base réalisable finale
					ArrayList<String> solBaseFinale = modelePanneau2.getProblemeLineaire().afficheSolutionBaseRealisableEtape(modelePanneau2.getProblemeLineaire().getNbSolutionBaseRealisable() - 1);
					decalText += ESPACEINTERLIGNE;
					for (int i = 0; i < solBaseFinale.size(); i++) {
						g2d.drawString(solBaseFinale.get(i), DECALMATHORIZONTAL, decalText);
						decalText += ESPACEINTERLIGNE;
					}
					
					decalText += ESPACEINTERLIGNE;
					g2d.drawString("La fonction objectif ayant comme valeur en ce point : "+modelePanneau2.getProblemeLineaire().getValeurFonctionObjectifEtape(modelePanneau2.getProblemeLineaire().getnombreDeMatrices() - 1), DECALMATHORIZONTAL, decalText);
					decalText += ESPACEINTERLIGNE;
					g2d.drawString("La méthode du simplexe est terminée car il n'y a plus de nombre(s) negatif(s) dans la dernière ligne de la matrice.", DECALMATHORIZONTAL, decalText);
					decalText += ESPACEINTERLIGNE;
					g2d.drawString("(Exception possible : dernière colonne).", DECALMATHORIZONTAL, decalText);
					decalText += ESPACEINTERLIGNE;
					
					//Réglage de la taille max de la fenêtre
					maxX = (int)(nbColonnes*TAILLECOL+DECALMATHORIZONTAL+75f);
					maxY = (int)decalText;
				}
				//Les étape intermédiaires : affichage matrices n et n+1 + choix du pivot
				else {
					dessineIntitules(g2d, 0.0f, 0.0f, true);
					dessineTout(g2d, 0.0f, ESPACEINTITULEMATRICE);
					
					float decalText = (nbLignes*TAILLELIGNE)*3+200f;
					g2d.drawString("Nouvelle valeur pour la fonction objectif : "+modelePanneau2.getProblemeLineaire().getValeurFonctionObjectifEtape(modelePanneau2.getNumEtapeCourant()), DECALMATHORIZONTAL, decalText);
					decalText += ESPACEINTERLIGNE;
					g2d.drawString("Avec les valeurs : ", DECALMATHORIZONTAL, decalText);
					
					//Affichage de la solution de base réalisable courante
					ArrayList<String> solBase = modelePanneau2.getProblemeLineaire().afficheSolutionBaseRealisableEtape(modelePanneau2.getNumEtapeCourant());
					decalText += ESPACEINTERLIGNE;
					for (int i = 0; i < solBase.size(); i++) {
						g2d.drawString(solBase.get(i), DECALMATHORIZONTAL, decalText);
						decalText += ESPACEINTERLIGNE;
					}
					
					//Réglage de la taille max de la fenêtre
					maxX = (int)(nbColonnes*TAILLECOL+DECALMATHORIZONTAL+300f);
					maxY = (int)decalText;
				}
			//Problème non résolu
			} else {
				g2d.setFont(new Font("Helvetica", Font.BOLD, 16));
				g2d.drawString("Problème non résolu", getWidth()/2, getHeight()/2);
				
				//Réglage de la taille max de la fenêtre
				maxX = 400;
				maxY = 200;
			}
		} catch (IndiceIncorrectException e) {
			e.printStackTrace();
		}
		
		//Redimensionnement de l'affichage
		setPreferredSize(new Dimension(maxX, maxY));
		revalidate();
	}
	
	/**
	 * Affiche la matrice ainsi que certaines informations liées.
	 * @param g2d Le contexte graphique qui va recevoir l'affichage de la matrice.
	 * @param nbLignes Le nombre de lignes de la matrice.
	 * @param nbColonnes Le nombre de colonnes de la matrice.
	 * @throws IndiceIncorrectException Si on demande à accéder à une matrice en dehors des limites.
	 */
	public void dessineTout(Graphics2D g2d, float x, float y) throws IndiceIncorrectException {
		//Sauvegarde du contexte des transformations pour restauration ultérieure
		AffineTransform contexte = g2d.getTransform();
		//Placement de la matrice aux coordonnées choisies
		g2d.translate(x, y);
		
		//Dessin de la matrice augmentée supérieure
		dessineMatriceAugmenteeEtape(g2d, 0.0f, 0.0f, modelePanneau2.getNumEtapeCourant());
		//Dessin de la matrice augmentée inférieure
		float decalageMatricesuiv = (nbLignes*TAILLELIGNE)*2+110f;
		dessineMatriceAugmenteeEtape(g2d, 0.0f, decalageMatricesuiv, modelePanneau2.getNumEtapeCourant()+1);
		
		//------------------------------------------------------------------------------------------
		//Trace de la ligne et de la colonne du pivot supérieure
		int pivotLigne = modelePanneau2.getProblemeLineaire().getPivotLigneEtape(modelePanneau2.getNumEtapeCourant());
		int pivotColonne = modelePanneau2.getProblemeLineaire().getPivotColonneEtape(modelePanneau2.getNumEtapeCourant());
		
		g2d.setColor(new Color(0,200,0));
		//La ligne du pivot de la matrice supérieure
		g2d.draw(new Rectangle2D.Float(DECALMATHORIZONTAL+10f, pivotLigne*TAILLELIGNE+DECALMATVERTICAL, nbColonnes*TAILLECOL-DECALMATHORIZONTAL-10f, TAILLELIGNE));
		//La ligne du pivot de la matrice inférieure
		g2d.draw(new Rectangle2D.Float(DECALMATHORIZONTAL+10f, pivotLigne*TAILLELIGNE+decalageMatricesuiv+DECALMATVERTICAL, nbColonnes*TAILLECOL-DECALMATHORIZONTAL-10f, TAILLELIGNE));
		
		g2d.setColor(Color.ORANGE);
		//La colonne du pivot de la matrice supérieure
		g2d.draw(new Rectangle2D.Float(pivotColonne*TAILLECOL+DECALMATHORIZONTAL+10f, DECALMATVERTICAL, TAILLECOL-DECALMATHORIZONTAL, nbLignes*TAILLELIGNE));
		//La colonne du pivot de la matrice inférieure
		g2d.draw(new Rectangle2D.Float(pivotColonne*TAILLECOL+DECALMATHORIZONTAL+10f, decalageMatricesuiv+DECALMATVERTICAL, TAILLECOL-DECALMATHORIZONTAL, nbLignes*TAILLELIGNE));
		//------------------------------------------------------------------------------------------
		
		//------------------------------------------------------------------------------------------
		//Affichage des ratios de la matrice supérieure
		g2d.setColor(Color.BLACK);
		g2d.setFont(new Font("Helvetica", Font.ITALIC, 14));
		int nbColMatAug = modelePanneau2.getProblemeLineaire().getNbColonnesMatriceAugmentee();
		ArrayList<String> ratiosMatSup = modelePanneau2.getProblemeLineaire().getRatioMatriceEtape(modelePanneau2.getNumEtapeCourant());
		for (int l = 0; l < ratiosMatSup.size(); l++) g2d.drawString(ratiosMatSup.get(l), DECALMATHORIZONTAL+nbColMatAug*TAILLECOL+20f, (l*TAILLELIGNE+20f)+DECALMATVERTICAL);
		//------------------------------------------------------------------------------------------
		
		//------------------------------------------------------------------------------------------
		//Affichage du pivotage
		float xCadre = pivotColonne*TAILLECOL+TAILLECOL;
		float yCadre = nbLignes*TAILLELIGNE+DECALMATVERTICAL+50f;
		float hauteurCadre = TAILLELIGNE*nbLignes;
		float largeurCadre = TAILLECOL+100f;
		
		g2d.setColor(Color.ORANGE);
		//La ligne de jointure avec la matrice supérieure
		g2d.draw(new Line2D.Float(DECALMATHORIZONTAL+pivotColonne*TAILLECOL+TAILLECOL/2, nbLignes*TAILLELIGNE+DECALMATVERTICAL, xCadre+largeurCadre/2, yCadre));
		//Le cadre pour afficher les résultats
		g2d.draw(new Rectangle2D.Float(xCadre, yCadre, largeurCadre, hauteurCadre));
		//La ligne de jointure avec la matrice inférieure
		g2d.draw(new Line2D.Float(xCadre+largeurCadre/2, yCadre+hauteurCadre, DECALMATHORIZONTAL+pivotColonne*TAILLECOL+TAILLECOL/2, decalageMatricesuiv+DECALMATVERTICAL));
		
		g2d.setColor(Color.BLACK);
		//Les formules de pivotage
		ArrayList<String> pivotEtape = modelePanneau2.getProblemeLineaire().getPivotageEtape(modelePanneau2.getNumEtapeCourant());
		for (int l = 0; l < pivotEtape.size(); l++) g2d.drawString(pivotEtape.get(l), xCadre+10f, (l*TAILLELIGNE+20f)+yCadre);
		//------------------------------------------------------------------------------------------
		
		//Restauration du contexte
		g2d.setTransform(contexte);
	}
	
	/**
	 * Permet de dessiner la matrice augmentee aux cordonnees x et y à l'étape numeroEtape.
	 * @param g2d Le contexte graphique pour le dessin de la matrice.
	 * @param x L'abscisse pour l'affichage de la matrice.
	 * @param y L'ordonnée pour l'affichage de la matrice.
	 * @param numeroEtape L'étape pour laquelle on désire
	 * @throws IndiceIncorrectException
	 */
	public void dessineMatriceAugmenteeEtape(Graphics2D g2d, float x, float y, int numeroEtape) throws IndiceIncorrectException {
		//Sauvegarde du contexte des transformations pour restauration ultérieure
		AffineTransform contexte = g2d.getTransform();
		//Placement de la matrice aux coordonnées choisies
		g2d.translate(x, y);
		
		//------------------------------------------------------------------------------------------
		//Trace des accolades de la matrice
		BasicStroke stroke = new BasicStroke(2f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
		g2d.setStroke(stroke);
		//Dessin de l'accolade de gauche
		g2d.draw(new QuadCurve2D.Float(DECALMATHORIZONTAL, DECALMATVERTICAL, DECALMATHORIZONTAL/2, (nbLignes*TAILLELIGNE)/2+DECALMATVERTICAL, DECALMATHORIZONTAL, nbLignes*TAILLELIGNE+DECALMATVERTICAL));
		//Dessin de l'accolade de droite
		g2d.draw(new QuadCurve2D.Float(nbColonnes*TAILLECOL+10f, DECALMATVERTICAL, nbColonnes*TAILLECOL+20f, (nbLignes*TAILLELIGNE)/2+DECALMATVERTICAL, nbColonnes*TAILLECOL+10f, nbLignes*TAILLELIGNE+DECALMATVERTICAL));
		//------------------------------------------------------------------------------------------
		
		//------------------------------------------------------------------------------------------
		//Trace des valeurs de la matrice
		g2d.setColor(Color.BLACK);
		g2d.setFont(new Font("Helvetica", Font.PLAIN, 14));
		int i = 0, j = 0;
		for (ArrayList<Float> ligneMatrice : modelePanneau2.getProblemeLineaire().getMatriceAugmenteeEtape(numeroEtape)) {
			for (Float valeur : ligneMatrice) {
				g2d.drawString(valeur+"", (j*TAILLECOL+20f)+DECALMATHORIZONTAL, (i*TAILLELIGNE+20f)+DECALMATVERTICAL);
				j++;
			}
			i++;
			j = 0;
		}
		//------------------------------------------------------------------------------------------
		
		//Restauration du contexte
		g2d.setTransform(contexte);
	}
	
	/**
	 * Permet de dessiner les intitulés des colonnes de matrice.
	 * @param g2d Le contexte graphique pour le dessin des intitulés de colonne de matrice.
	 * @param x L'abscisse pour l'affichage des intitulés de colonne de matrice.
	 * @param y L'ordonnée pour l'affichage des intitulés de colonne de matrice.
	 * @param avecRatio Indique si l'intitulé de la colonne "calcul des ratios" doit
	 * apparaitre ou non.
	 * @throws IndiceIncorrectException
	 */
	public void dessineIntitules(Graphics2D g2d, float x, float y, boolean avecRatio) throws IndiceIncorrectException {
		//Sauvegarde du contexte des transformations pour restauration ultérieure
		AffineTransform contexte = g2d.getTransform();
		//Placement de la matrice aux coordonnées choisies
		g2d.translate(x, y);
		//Reglage de la fonte pour les intitulés
		g2d.setFont(new Font("Helvetica", Font.BOLD, 14));
		
		//Le décalage par défaut des intitulés par rapport aux colonnes
		float decalageIntituleCol = 20f;
		
		//------------------------------------------------------------------------------------------
		//Trace des intitulés des colonnes
		int dimension = modelePanneau2.getProblemeLineaire().getDimension();
		int nbColMatAug = modelePanneau2.getProblemeLineaire().getNbColonnesMatriceAugmentee();
		g2d.setColor(Color.BLACK);
		
		//Les intitulés des variables nommées (hors base)
		for (int i = 0; i < dimension; i++) {
			g2d.drawString(modelePanneau2.getProblemeLineaire().getNomVariable(i), DECALMATHORIZONTAL+i*TAILLECOL+decalageIntituleCol, DECALMATVERTICAL);
		}
		
		//Les intitulés des variables supplémentaires (variables de base)
		char c = 'M';
		for (int i = dimension; i < nbLignes+dimension; i++) {
			g2d.drawString(c++ + "", DECALMATHORIZONTAL+i*TAILLECOL+decalageIntituleCol, DECALMATVERTICAL);
		}
		
		//Intitulé de la colonne "réponse"
		g2d.drawString("Réponse", DECALMATHORIZONTAL+(dimension+nbLignes)*TAILLECOL+decalageIntituleCol, DECALMATVERTICAL);
		//Intitulé de la colonne "calcul des ratios"
		if (avecRatio) g2d.drawString("Calcul des ratios", DECALMATHORIZONTAL+nbColMatAug*TAILLECOL+decalageIntituleCol, DECALMATVERTICAL);
		//------------------------------------------------------------------------------------------
		
		//Restauration du contexte
		g2d.setTransform(contexte);
	}
	
	/**
	 * Permet d'obtenir sous forme d'images la succession des étapes réalisées pendant la résolution
	 * par la méthode du simplexe.
	 * @return Une collection contenant la succession des images.
	 */
	public List<Image> getListPanneauImage() {
		ArrayList<Image> listImage = new ArrayList<Image>();

		for (int i = -1; i < (modelePanneau2.getProblemeLineaire().getnombreDeMatrices() - 1); i++) {
			modelePanneau2.setNumEtapeCourant(i);
			int width = getWidth();
			int height = getHeight();
			BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			Graphics2D g = image.createGraphics();
			paintAll(g);
			g.dispose();
			listImage.add(image);
		}

		return listImage;
	}
}
